#ifdef _WIN32
#include "CNetServiceImpl.h"
#include <chrono>
#include "../coroutine.h"

namespace TB
{
	CNetServiceImpl::CWsaData CNetServiceImpl::m_oWsaData;

	CNetServiceImpl::CNetServiceImpl()
	{
	}

	CNetServiceImpl::~CNetServiceImpl()
	{
		Close();
	}

	bool CNetServiceImpl::Init(const std::weak_ptr<CCoroutine>& poCoroutine, const std::function<void(INT32)>& rcbOnPollError, const std::weak_ptr<CNetServiceImpl>& poSelf)
	{
		if (!m_poCoroutine.expired())
			return false;
		auto poSharedPtr = poCoroutine.lock();
		if (!poSharedPtr)
			return false;
		m_poCoroutine = poCoroutine;
		m_cbOnPollError = rcbOnPollError;
		m_poSelf = poSelf;
		poSharedPtr->Post([poSelf]()
		{
			auto poImpl = poSelf.lock();
			if (poImpl == nullptr)
				return;
			poImpl->Poll();
		});
		return true;
	}

	void CNetServiceImpl::Poll()
	{
		if (m_sTimerUID)
			return;
		auto poCoroutine = GetCoroutine().lock();
		if (!poCoroutine)
			return;
		m_sTimerUID = poCoroutine->GetTimerMgr().StartTimer(1, 0, 0, [this](STimerUID, UINT32, UINT32)
		{
			INT32 dwCount = 0;
			do
			{
				dwCount = _Poll();
				if (dwCount < 0)
				{
					if (m_cbOnPollError)
					{
						int dwErr = WSAGetLastError();
						m_cbOnPollError(dwErr);
					}
					return;
				}
			} while (dwCount != 0);
		});
	}

	INT32 CNetServiceImpl::_Poll()
	{
		if (m_oPollFDSet.size() != 0)
		{
			INT32 dwPollCount = WSAPoll(m_oPollFDSet.data(), m_oPollFDSet.size(), 0);
			if (dwPollCount < 0)
				return dwPollCount;
			if (dwPollCount != 0)
			{
				for (std::size_t i = 0; i != m_oPollFDSet.size(); ++i)
				{
					if (m_oPollFDSet[i].revents == 0)
						continue;
					m_oPolledFDCache.emplace_back(m_oPollFDSet[i]);
					m_oPollFDSet[i].revents = 0;
				}
			}
		}
		INT32 dwRet = 0;
		for(auto& roInfo : m_oPolledFDCache)
		{
			auto it = m_oPollCallbacks.find(roInfo.fd);
			if (it == m_oPollCallbacks.end())
				continue;
			DOnNetPollEvent& rcbCallback = it->second;
			if (rcbCallback)
				rcbCallback(roInfo.fd, roInfo.revents);
			++dwRet;
		}
		m_oPolledFDCache.clear();
		return dwRet;
	}

	bool CNetServiceImpl::AddFD(SOCKET sSocket, SHORT wEvents, const DOnNetPollEvent& cbOnPollEvent)
	{
		if (!m_oPollCallbacks.insert({ sSocket, cbOnPollEvent }).second)
			return false;
		WSAPOLLFD& sWSAPOLLFD = m_oPollFDSet.emplace_back();
		sWSAPOLLFD.fd = sSocket;
		sWSAPOLLFD.events = wEvents;
		return true;
	}

	bool CNetServiceImpl::ModFD(SOCKET sSocket, SHORT wEvents)
	{
		for (auto& roInfo : m_oPollFDSet)
		{
			if (roInfo.fd == sSocket)
			{
				roInfo.events = wEvents;
				return true;
			}
		}
		return false;
	}

	bool CNetServiceImpl::DelFD(SOCKET sSocket)
	{
		for (auto it = m_oPollFDSet.begin(); it != m_oPollFDSet.end(); ++it)
		{
			if (it->fd == sSocket)
			{
				m_oPollCallbacks.erase(sSocket);
				m_oPollFDSet.erase(it);
				return true;
			}
		}
		return true;
	}

	void CNetServiceImpl::Close()
	{
		if (m_sTimerUID)
		{
			auto poCoroutine = GetCoroutine().lock();
			if (poCoroutine)
				poCoroutine->GetTimerMgr().StopTimer(m_sTimerUID);
			m_sTimerUID = STimerUID();
		}
	}
}
#endif